package util;

/*
 * David Zielinski: dzielin | Updated: 11.29.11 -
 *
 */

import java.io.IOException;
import java.net.URL;
import java.net.MalformedURLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

import com.google.gdata.client.calendar.*;
import com.google.gdata.data.calendar.*;
import com.google.gdata.data.extensions.When;
import com.google.gdata.data.extensions.Where;
import com.google.gdata.data.extensions.ExtendedProperty;
import com.google.gdata.data.*;
import com.google.gdata.util.*;

/*
 * The CalendarInterface class allows the program to communicate with Google
 * Calendar via the Google Calendar and GData APIs.
 */
public class CalendarInterface {
	/*
	 * The MealEvent class is used to hold meal name, description and date.
	 * This allows functions of the CalendarInterface to return easily-usable
	 * objects.
	 */
	public static class MealEvent
	{
		private Date mealEventDate;
		private String mealEventName;
		private String mealEventDescription;
		private String src="GEN";

		/*
		 * Set the date of the meal event.
		 * @param mealDate date of meal event.
		 */
		public void setDate(Date mealDate)
		{
			mealEventDate = mealDate;
		}

		/*
		 * Set the name of the meal event.
		 * @param mealName name of meal event.
		 */
		public void setMealName(String mealName)
		{
			mealEventName = mealName;
		}

		/*
		 * Set the description of the meal event.
		 * @param mealDescription description of meal event.
		 */
		public void setDescription(String mealDescription)
		{
			mealEventDescription = mealDescription;
		}

		/*
		 * Returns the date of the meal event.
		 * @return the date of the meal event.
		 */
		public Date getDate()
		{
			return mealEventDate;
		}

		/*
		 * Returns the name of the meal event.
		 * @return the name of the meal event.
		 */
		public String getMealName()
		{
			return mealEventName;
		}

		/*
		 * Returns the description of the meal event.
		 * @return the description of the meal event.
		 */
		public String getDescription()
		{
			return mealEventDescription;
		}

		/*
		 * Returns the source of the meal event.
		 * @return the source of the meal event.
		 */
		public String getSource() {
			return this.src;
		}

		/*
		 * Set the source of the meal event.
		 * @param source of meal event.
		 */
		public void setSource(String src) {
			this.src=src;
		}

		/*
		 * Returns a string representation of this Meal Event. Of the form
		 * "<code>mealName</code>".
		 * @return a string representation of this Meal Event.
		 */
		public String toString() {
			return mealEventName;
		}
	}

	private static final String METAFEED_URL_BASE = "https://www.google.com/calendar/feeds/";
	private static final String OWNCALENDARS_FEED_URL_SUFFIX = "/owncalendars/full";
	private static final String calendarDescription = "This calendar keeps track of meals for any given day.";
	private static final String calendarName = "Meals";

	private static CalendarService googleCalendarService;
	private static CalendarEntry googleCalendar;
	private static String googleUserName = "dazgarbage@gmail.com";
	private static String googlePassword = "ThisIsAPassword";
	private static String calendarTimeZone = "America/Los_Angeles";
	private static String calendarLocation = "West Lafayette";
	private static String applicationName = "arroz";
	private static String calendarColor = "#A32929";

	//David Zielinski: dzielin | Last Updated: 11.29.11
	//Task: initService method that returns true or false if authenticated
	/*
	 * Returns boolean value for authentication success.
	 * @return boolean representing authentication success or failure.
	 */
	public static boolean initService()
	{
		return initService(googleUserName, googlePassword);
	}

	//David Zielinski: dzielin | Last Updated: 11.29.11
	//Task: initService method that returns true or false if authenticated
	//      accepts username and password.
	/*
	 * Returns boolean value for authentication success after attempting to log
	 * in using userName and password.
	 * @return boolean representing authentication success or failure.
	 */
	public static boolean initService(String username, String password)
	{
		try {
			googleCalendarService = new CalendarService(applicationName);
			googleCalendarService.setUserCredentials(username, password);
			return true;
		}
		catch (AuthenticationException e)
		{
			return false;
		}
	}

	//David Zielinski: dzielin | Last Updated: 11.29.11
	//Task: createCalendar method that creates a calendar based
	//      on constants.
	/*
	 * Creates a calendar using the class' constant (calendarName).
	 */
	private static CalendarEntry createCalendar()
	{
		// Create the calendar
		CalendarEntry calendar = new CalendarEntry();
		calendar.setTitle(new PlainTextConstruct(calendarName));
		calendar.setSummary(new PlainTextConstruct(calendarDescription));
		calendar.setTimeZone(new TimeZoneProperty(calendarTimeZone));
		calendar.setHidden(HiddenProperty.FALSE);
		calendar.setColor(new ColorProperty(calendarColor));
		calendar.addLocation(new Where("","",calendarLocation));

		// Insert the calendar
		URL postUrl = null;
		try {
			postUrl = new URL("https://www.google.com/calendar/feeds/default/owncalendars/full");
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		CalendarEntry returnedCalendar = null;
		try {
			returnedCalendar = googleCalendarService.insert(postUrl, calendar);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ServiceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		System.out.println("Created Calendar:\t\t\"" + returnedCalendar.getTitle().getPlainText() + "\"");

		return returnedCalendar;
	}

	//David Zielinski: dzielin | Last Updated: 11.29.11
	//Task: selectCalendar method
	/*
	 * selectCalendar method that selects the calendar with the given name from
	 * the class' constants.  If it does not exist, createCalendar is called
	 * and the result is selected.
	 */
	public static void selectCalendar()
	{
		CalendarEntry calendar = null;
		// Send the request and receive the response:
		URL feedUrl = null;
		try {
			feedUrl = new URL(METAFEED_URL_BASE + googleUserName + OWNCALENDARS_FEED_URL_SUFFIX);
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		CalendarFeed resultFeed = null;
		try {
			resultFeed = googleCalendarService.getFeed(feedUrl, CalendarFeed.class);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ServiceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// Delete calendar matching the title CALENDAR_NAME
		for (int i = 0; i < resultFeed.getEntries().size(); i++) {
			CalendarEntry entry = resultFeed.getEntries().get(i);

			if (entry.getTitle().getPlainText().equals(calendarName)){
				calendar = entry;
			}
		}

		if (calendar == null)
		{
			calendar = createCalendar();
		}

		googleCalendar = calendar;
	}

	//David Zielinski: dzielin | Last Updated: 11.29.11
	//Task: deleteCalendar method
	/*
	 * Deletes a calendar using the class' constant (calendarName).
	 */
	public static void deleteCalendar()
	{
		// Send the request and receive the response:
		URL feedUrl = null;
		try {
			feedUrl = new URL(METAFEED_URL_BASE + googleUserName + OWNCALENDARS_FEED_URL_SUFFIX);
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		CalendarFeed resultFeed = null;
		try {
			resultFeed = googleCalendarService.getFeed(feedUrl, CalendarFeed.class);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ServiceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// Delete calendar matching the title CALENDAR_NAME
		for (int i = 0; i < resultFeed.getEntries().size(); i++)
		{
			CalendarEntry entry = resultFeed.getEntries().get(i);

			if (entry.getTitle().getPlainText().equals(calendarName))
			{
				try {
					entry.delete();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (ServiceException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	//David Zielinski: dzielin | Last Updated: 11.29.11
	//Task: getCalendarFeedURL method using the currently selected
	//      calendar.
	/*
	 * Returns the URL of the selected calendar's feed.
	 * @return URL of selected calendar's feed.
	 */
	private static URL getCalendarFeedURL()
	{
		String calendarID = googleCalendar.getId().replace(googleCalendar.getId().substring(0, googleCalendar.getId().indexOf("/calendars/") + "/calendars/".length()), "");
		String calendarString = "http://www.google.com/calendar/feeds/" + calendarID + "/private/full/";

		URL feedUrl = null;
		try {
			feedUrl = new URL(calendarString);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		return feedUrl;
	}

	//David Zielinski: dzielin | Last Updated: 11.29.11
	//Task: getAllMeals method
	/*
	 * Returns an array of all meals on the selected calendar
	 * @return MealEvent[] of all meals.
	 */

	public static MealEvent[] getAllMeals()
	{

		CalendarEventFeed resultFeed = null;
		try {
			resultFeed = googleCalendarService.getFeed(getCalendarFeedURL(), CalendarEventFeed.class);
		} catch (IOException e) {

			e.printStackTrace();
		} catch (ServiceException e) {

			e.printStackTrace();
		}

		MealEvent meals[] = new MealEvent[resultFeed.getEntries().size()];
		int eventCount = 0;
		for (CalendarEventEntry calEntry : resultFeed.getEntries())
		{
			Date eventTime = null;
			try
			{
				eventTime = new SimpleDateFormat("yyyy-MM-dd").parse(calEntry.getTimes().get(0).getStartTime().toString());
			}
			catch (java.text.ParseException e)
			{
				e.printStackTrace();
			}
			meals[eventCount] = new MealEvent();
			meals[eventCount].setMealName(calEntry.getTitle().getPlainText());
			meals[eventCount].setDate(eventTime);
			meals[eventCount].setDescription(calEntry.getPlainTextContent());
			if(calEntry.getExtendedProperty().size()>=1)
				for(ExtendedProperty property : calEntry.getExtendedProperty())
					if(property.getName().equals("ARROZ_SRC"))
						meals[eventCount].setSource(property.getValue());

			eventCount++;
		}
		return meals;
	}


	//David Zielinski: dzielin | Last Updated: 11.29.11
	//Task: getMealsBetween method
	/*
	 * Returns an array of all meals on the selected calendar between
	 * startDate and endDate.
	 * @param startDate minimum date to query.
	 * @param endDate maximum date to query.
	 * @return MealEvent[] of all meals.
	 */
	public static MealEvent[] getMealsBetween(Date startDate, Date endDate)
	{
		int eventCount = 0;

		DateFormat formatter;
		formatter = new SimpleDateFormat("yyyy-MM-dd");
		String startDateString = formatter.format(startDate) + "T00:00:00";
		String endDateString = formatter.format(endDate) + "T23:59:59";

		CalendarQuery myQuery = new CalendarQuery(getCalendarFeedURL());
		myQuery.setMinimumStartTime(DateTime.parseDateTime(startDateString));
		myQuery.setMaximumStartTime(DateTime.parseDateTime(endDateString));
		myQuery.setStringCustomParameter("orderby", "starttime");
		myQuery.setStringCustomParameter("sortorder", "a");
		myQuery.setIntegerCustomParameter("max-results", 50);

		CalendarEventFeed resultFeed = null;
		try {
			resultFeed = googleCalendarService.query(myQuery, CalendarEventFeed.class);
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (ServiceException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		MealEvent meals[] = new MealEvent[resultFeed.getEntries().size()];
		eventCount = 0;
		for (CalendarEventEntry calEntry : resultFeed.getEntries()) {
			Date eventTime = null;
			try {
				eventTime = new SimpleDateFormat("yyyy-MM-dd").parse(calEntry.getTimes().get(0).getStartTime().toString());
			} catch (java.text.ParseException e) {
				e.printStackTrace();
			}
			meals[eventCount] = new MealEvent();
			meals[eventCount].setMealName(calEntry.getTitle().getPlainText());
			meals[eventCount].setDate(eventTime);
			meals[eventCount].setDescription(calEntry.getPlainTextContent());
			if(calEntry.getExtendedProperty().size()>=1)
				for(ExtendedProperty property : calEntry.getExtendedProperty())
					if(property.getName().equals("ARROZ_SRC"))
						meals[eventCount].setSource(property.getValue());

			eventCount++;
		}
		return meals;
	}

	//David Zielinski: dzielin | Last Updated: 11.29.11
	//Task: deleteMealsByDate method
	/*
	 * Returns an array of all meals on the selected calendar between
	 * startDate and endDate.
	 * @param startDate minimum date to query.
	 * @param endDate maximum date to query.
	 * @return MealEvent[] of all meals.
	 */
	public static void deleteMealsByDate(Date mealDate)
	{
		DateFormat formatter;
		formatter = new SimpleDateFormat("yyyy-MM-dd");
		String startDateString = formatter.format(mealDate) + "T00:00:00";
		String endDateString = formatter.format(mealDate) + "T23:59:59";

		CalendarQuery myQuery = null;
		myQuery = new CalendarQuery(getCalendarFeedURL());
		myQuery.setMinimumStartTime(DateTime.parseDateTime(startDateString));
		myQuery.setMaximumStartTime(DateTime.parseDateTime(endDateString));
		myQuery.setStringCustomParameter("orderby", "starttime");
		myQuery.setStringCustomParameter("sortorder", "a");

		CalendarEventFeed resultFeed = null;
		try {
			resultFeed = googleCalendarService.query(myQuery, CalendarEventFeed.class);
		} catch (IOException e) {
			System.out.println("IO Exception in deleteMealsByDate() while getting feed.");
			e.printStackTrace();
		} catch (ServiceException e) {
			System.out.println("ServiceException in deleteMealsByDate() while getting feed.");
			e.printStackTrace();
		}

		for (CalendarEventEntry calEntry : resultFeed.getEntries())
		{
			try {
				calEntry.delete();
			} catch (IOException e) {
				System.out.println("IO Exception in deleteMealsByDate() while deleting.");
				e.printStackTrace();
			} catch (ServiceException e) {
				System.out.println("ServiceException in deleteMealsByDate() while deleting.");
				e.printStackTrace();
			}
		}

	}

	//David Zielinski: dzielin | Last Updated: 11.30.11
	//Task: addMealEvent method
	/*
	 * Add MealEvent to selected calendar.
	 * @param MealEvent meal to add to selected calendar.
	 */
	public static void addMealEvent(MealEvent meal)
	{
		CalendarEventEntry myEntry = new CalendarEventEntry();

		ExtendedProperty property = new ExtendedProperty();
		property.setName("ARROZ_SRC");
		property.setValue(meal.getSource());
		myEntry.addExtendedProperty(property);

		myEntry.setTitle(new PlainTextConstruct(meal.getMealName()));
		myEntry.setContent(new PlainTextConstruct(meal.getDescription()));

		DateFormat formatter;
		formatter = new SimpleDateFormat("yyyy-MM-dd");
		String startDateString = formatter.format(meal.getDate()) + "T00:00:00";
		String endDateString = formatter.format(meal.getDate()) + "T00:00:00";

		DateTime startDate = DateTime.parseDateTime(startDateString);
		startDate.setDateOnly(true);
		DateTime endDate = DateTime.parseDateTime(endDateString);
		endDate.setDateOnly(true);
		When eventTimes = new When();
		eventTimes.setStartTime(startDate);
		eventTimes.setEndTime(endDate);
		myEntry.addTime(eventTimes);

		// Send the request and receive the response:
		try
		{
			CalendarEventEntry insertedEntry = googleCalendarService.insert(getCalendarFeedURL(), myEntry);
		}
		catch (MalformedURLException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch (ServiceException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
